!-------------------------------------------------------------------
! manages reading and interpolation of prescribed ghg tracers
! Created by: Francis Vitt
!-------------------------------------------------------------------
module prescribed_ghg

  use shr_kind_mod,     only : r8 => shr_kind_r8
  use cam_abortutils,   only : endrun
  use spmd_utils,       only : masterproc
  use tracer_data,      only : trfld, trfile
  use cam_logfile,      only : iulog

  implicit none
  private
  save 

  type(trfld), pointer :: fields(:)
  type(trfile)         :: file

  public :: prescribed_ghg_init
  public :: prescribed_ghg_adv
  public :: write_prescribed_ghg_restart
  public :: read_prescribed_ghg_restart
  public :: has_prescribed_ghg
  public :: prescribed_ghg_register
  public :: init_prescribed_ghg_restart
  public :: prescribed_ghg_readnl

  logical :: has_prescribed_ghg = .false.
  integer, parameter, public :: N_GHG = 5
  integer :: number_flds

  character(len=256) :: filename = 'NONE'
  character(len=256) :: filelist = ''
  character(len=256) :: datapath = ''
  character(len=32)  :: datatype = 'SERIAL'
  logical            :: rmv_file = .false.
  integer            :: cycle_yr  = 0
  integer            :: fixed_ymd = 0
  integer            :: fixed_tod = 0
  character(len=16)  :: specifier(N_GHG) = ''

  character(len=8)    :: ghg_names(N_GHG) = (/ 'prsd_co2',  'prsd_ch4',  'prsd_n2o',  'prsd_f11',  'prsd_f12'  /)
  real(r8), parameter :: molmass(N_GHG)   = (/ 44.00980_r8, 16.04060_r8, 44.01288_r8, 137.3675_r8, 120.9132_r8 /)

  integer :: index_map(N_GHG)

contains


!-------------------------------------------------------------------
!-------------------------------------------------------------------
subroutine prescribed_ghg_readnl(nlfile)

   use namelist_utils,  only: find_group_name
   use units,           only: getunit, freeunit
   use mpishorthand

   character(len=*), intent(in) :: nlfile  ! filepath for file containing namelist input

   ! Local variables
   integer :: unitn, ierr
   character(len=*), parameter :: subname = 'prescribed_ghg_readnl'

   character(len=16)  :: prescribed_ghg_specifier(N_GHG)
   character(len=256) :: prescribed_ghg_file
   character(len=256) :: prescribed_ghg_filelist
   character(len=256) :: prescribed_ghg_datapath
   character(len=32)  :: prescribed_ghg_type
   logical            :: prescribed_ghg_rmfile
   integer            :: prescribed_ghg_cycle_yr
   integer            :: prescribed_ghg_fixed_ymd
   integer            :: prescribed_ghg_fixed_tod

   namelist /prescribed_ghg_nl/ &
      prescribed_ghg_specifier, &
      prescribed_ghg_file,      &
      prescribed_ghg_filelist,  &
      prescribed_ghg_datapath,  &
      prescribed_ghg_type,      &
      prescribed_ghg_rmfile,    &
      prescribed_ghg_cycle_yr,  &
      prescribed_ghg_fixed_ymd, &
      prescribed_ghg_fixed_tod      
   !-----------------------------------------------------------------------------

   ! Initialize namelist variables from local module variables.
   prescribed_ghg_specifier= specifier
   prescribed_ghg_file     = filename
   prescribed_ghg_filelist = filelist
   prescribed_ghg_datapath = datapath
   prescribed_ghg_type     = datatype
   prescribed_ghg_rmfile   = rmv_file
   prescribed_ghg_cycle_yr = cycle_yr
   prescribed_ghg_fixed_ymd= fixed_ymd
   prescribed_ghg_fixed_tod= fixed_tod

   ! Read namelist
   if (masterproc) then
      unitn = getunit()
      open( unitn, file=trim(nlfile), status='old' )
      call find_group_name(unitn, 'prescribed_ghg_nl', status=ierr)
      if (ierr == 0) then
         read(unitn, prescribed_ghg_nl, iostat=ierr)
         if (ierr /= 0) then
            call endrun(subname // ':: ERROR reading namelist')
         end if
      end if
      close(unitn)
      call freeunit(unitn)
   end if

#ifdef SPMD
   ! Broadcast namelist variables
   call mpibcast(prescribed_ghg_specifier,len(prescribed_ghg_specifier(1))*N_GHG,     mpichar, 0, mpicom)
   call mpibcast(prescribed_ghg_file,     len(prescribed_ghg_file),     mpichar, 0, mpicom)
   call mpibcast(prescribed_ghg_filelist, len(prescribed_ghg_filelist), mpichar, 0, mpicom)
   call mpibcast(prescribed_ghg_datapath, len(prescribed_ghg_datapath), mpichar, 0, mpicom)
   call mpibcast(prescribed_ghg_type,     len(prescribed_ghg_type),     mpichar, 0, mpicom)
   call mpibcast(prescribed_ghg_rmfile,   1, mpilog,  0, mpicom)
   call mpibcast(prescribed_ghg_cycle_yr, 1, mpiint,  0, mpicom)
   call mpibcast(prescribed_ghg_fixed_ymd,1, mpiint,  0, mpicom)
   call mpibcast(prescribed_ghg_fixed_tod,1, mpiint,  0, mpicom)
#endif

   ! Update module variables with user settings.
   specifier  = prescribed_ghg_specifier
   filename   = prescribed_ghg_file
   filelist   = prescribed_ghg_filelist
   datapath   = prescribed_ghg_datapath
   datatype   = prescribed_ghg_type
   rmv_file   = prescribed_ghg_rmfile
   cycle_yr   = prescribed_ghg_cycle_yr
   fixed_ymd  = prescribed_ghg_fixed_ymd
   fixed_tod  = prescribed_ghg_fixed_tod

   ! Turn on prescribed volcanics if user has specified an input dataset.
   if (len_trim(filename) > 0 .and. filename.ne.'NONE') has_prescribed_ghg = .true.

end subroutine prescribed_ghg_readnl

!-------------------------------------------------------------------
!-------------------------------------------------------------------
  subroutine prescribed_ghg_register()
    use ppgrid,         only: pver, pcols
    use physics_buffer, only : pbuf_add_field, dtype_r8

    integer :: i,idx

    if (has_prescribed_ghg) then
       do i = 1,N_GHG
          call pbuf_add_field(ghg_names(i),'physpkg',dtype_r8,(/pcols,pver/),idx)
       enddo
    endif

  endsubroutine prescribed_ghg_register
!-------------------------------------------------------------------
!-------------------------------------------------------------------
  subroutine prescribed_ghg_init()

    use tracer_data, only : trcdata_init
    use cam_history, only : addfld

    implicit none

    integer :: ndx, istat, i
    
    if ( has_prescribed_ghg ) then
       if ( masterproc ) then
          write(iulog,*) 'ghg is prescribed in :'//trim(filename)
       endif
    else
       return
    endif

    allocate(file%in_pbuf(size(specifier)))
    file%in_pbuf(:) = .true.
    call trcdata_init( specifier, filename, filelist, datapath, fields, file, &
                       rmv_file, cycle_yr, fixed_ymd, fixed_tod, datatype)
        
    number_flds = 0
    if (associated(fields)) number_flds = size( fields )

    if( number_flds < 1 ) then
       if ( masterproc ) then
          write(iulog,*) 'There are no prescribed ghg tracers'
          write(iulog,*) ' '
       endif
       return
    end if

    do i = 1,number_flds
       ndx = get_ndx( fields(i)%fldnam )
       index_map(i) = ndx

       if (ndx < 1) then
          call endrun('prescribed_ghg_init: '//trim(fields(i)%fldnam)//' is not one of the named ghg fields in pbuf2d')
       endif
       call addfld( fields(i)%fldnam, (/ 'lev' /), 'I','kg/kg', 'prescribed ghg' )
    enddo

  end subroutine prescribed_ghg_init

  subroutine prescribed_ghg_adv( state, pbuf2d)

    use tracer_data   , only: advance_trcdata
    use physics_types , only: physics_state
    use ppgrid        , only: begchunk, endchunk
    use ppgrid        , only: pcols, pver
    use string_utils  , only: to_lower, GLC
    use cam_history   , only: outfld
    use physconst     , only: mwdry, & ! molecular weight dry air ~ kg/kmole
                              boltz    ! J/K/molecule
    use physics_buffer, only: physics_buffer_desc, pbuf_get_field, pbuf_set_field, pbuf_get_chunk

    type(physics_state), intent(in)    :: state(begchunk:endchunk)                 
    
    type(physics_buffer_desc), pointer :: pbuf2d(:,:)

    type(physics_buffer_desc), pointer :: pbuf_chnk(:)
    integer ind, c, ncol, i
    real(r8) to_mmr (pcols,pver)
    real(r8) outdata(pcols,pver)
    real(r8), pointer :: tmpptr(:,:)

    character(32) units_str

    if (.not. has_prescribed_ghg) return

    call advance_trcdata(fields, file, state, pbuf2d)
    
    ! Set the correct units and invoke history outfld
    do i = 1,number_flds
      ind = index_map(i)

      units_str = trim(to_lower(trim(fields(i)%units(:GLC(fields(i)%units)))))

!$OMP PARALLEL DO PRIVATE (C, NCOL, OUTDATA, TO_MMR, tmpptr, pbuf_chnk)
      do c = begchunk, endchunk
        ncol = state(c)%ncol
        select case (units_str)
        case ('molec/cm3', '/cm3', 'molecules/cm3', 'cm^-3', 'cm**-3')
          to_mmr(:ncol,:) = (molmass(ind) * 1.e6_r8 * boltz * state(c)%t(:ncol,:)) / (mwdry * state(c)%pmiddry(:ncol,:))
        case ('kg/kg','mmr')
          to_mmr(:ncol,:) = 1._r8
        case ('mol/mol', 'mole/mole', 'vmr', 'fraction')
          to_mmr(:ncol,:) = molmass(ind) / mwdry
        case default
          write(iulog, *) 'prescribed_ghg_adv: units = ',trim(fields(i)%units) ,' are not recognized'
          call endrun('prescribed_ghg_adv: units are not recognized')
        end select

        pbuf_chnk => pbuf_get_chunk(pbuf2d, c)
        call pbuf_get_field(pbuf_chnk, fields(i)%pbuf_ndx, tmpptr)

        tmpptr(:ncol,:) = tmpptr(:ncol,:) * to_mmr(:ncol,:)

        outdata(:ncol,:) = tmpptr(:ncol,:) 
        call outfld(fields(1)%fldnam, outdata(:ncol,:), ncol, state(c)%lchnk)
      end do
    end do

  end subroutine prescribed_ghg_adv

  subroutine init_prescribed_ghg_restart( piofile )
    use pio, only : file_desc_t
    use tracer_data, only : init_trc_restart
    implicit none
    type(file_desc_t),intent(inout) :: pioFile     ! pio File pointer

    call init_trc_restart( 'prescribed_ghg', piofile, file )

  end subroutine init_prescribed_ghg_restart
!-------------------------------------------------------------------
  subroutine write_prescribed_ghg_restart( piofile )
    use tracer_data, only : write_trc_restart
    use pio, only : file_desc_t
    implicit none

    type(file_desc_t) :: piofile

    call write_trc_restart( piofile, file )

  end subroutine write_prescribed_ghg_restart

!-------------------------------------------------------------------
!-------------------------------------------------------------------
  subroutine read_prescribed_ghg_restart( pioFile )
    use tracer_data, only : read_trc_restart
    use pio, only : file_desc_t
    implicit none

    type(file_desc_t) :: piofile

    call read_trc_restart( 'prescribed_ghg', piofile, file )

  end subroutine read_prescribed_ghg_restart
!-------------------------------------------------------------------
  integer function get_ndx( name )

    implicit none
    character(len=*), intent(in) :: name

    integer :: i

    get_ndx = 0
    do i = 1,N_GHG
      if ( trim(name) == trim(ghg_names(i)) ) then
        get_ndx = i
        return
      endif
    enddo

  end function get_ndx

end module prescribed_ghg
